#include "Server.hpp"

namespace bagprod { namespace finance { namespace SPS {
    
//<editor-fold desc="Construtors/Initaliazers">
Server::Server()
{
    
}

void Server::Initialize(std::string host, int port)
{
    // Get Socket
    if( (socketFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Error creating socket");
        exit(1);
    }
    
    bzero( &server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(port);
    
    // Bind socket
    if( bind(socketFd, (struct sockaddr *) &server, sizeof(server)) < 0)
    {
        perror("Error binding socket");
        exit(1);
    }
    
    listen(socketFd, 5);
    
    CreateThreads();
}

void Server::CreateThreads()
{
    for(int i = 0; i < MAX_THREAD; i++)
    {
        mThreads[i].server = this;
        mThreads[i].client_count = 0;
        pthread_create(&mThreads[i].tid, NULL, 
                &server_thread_function, &mThreads[i]);
    }
}

//</editor-fold>

//<editor-fold desc="Server Looping & Helpers">
int Server::ChooseThread()
{
    int i = MAX_THREAD-1;
    int min = 0;
    
    while(i > -1)
    {
        if(mThreads[i].client_count < mThreads[i-1].client_count)
        {
            min = i;
        }
        i--;
    }
    return(min);
}
void Server::Run()
{
    int thread;
    int client;
    
    while(true)
    {
        client = accept(socketFd, NULL, NULL);
#ifdef _DEBUG
        std::cerr << "Server Accepted socket.\n";
        std::cerr << "Chosing thread.\n";
#endif
        thread = ChooseThread();
        
#ifdef _DEBUG
        std::cerr << "Server chose thread: " << thread << std::endl;
#endif
        for(int i = 0; i < MAX_CLIENT_PER_THREAD; i++)
        {
            if(mThreads[thread].clients[i] == 0)
            {
                mThreads[thread].clients[i] = client;
                mThreads[thread].client_count++;
                break;
            }
        }
        
    }
}

//</editor-fold>

//<editor-fold desc="Message handling threads">
void *server_thread_function(void *arg)
{
    Thread *thread = (Thread*)arg;
    
    memset((int*) &thread->clients, 0, sizeof(thread->clients));

#ifdef _DEBUG
    std::cerr << "Thread <" << thread->id << "> is entering loop\n";
#endif
    
    while(1)
    {
        sleep(1); // Allow other processes to run
    for(int i = 0; i < MAX_CLIENT_PER_THREAD; i++)
    {
        if(thread->clients[i] != 0)
        {
#ifdef _DEBUG
            std::cerr << "Thread is accepting request..\n";
#endif
            protocol::StockPublisherRequest *request = new 
                    protocol::StockPublisherRequest();
            
            request->ParseFromFileDescriptor(thread->clients[i]);
#ifdef _DEBUG
            std::cerr << "Accept Successful!\n";
#endif
            switch( request->requesttype() )
            {
                case protocol::STOCKSNAPSHOTREQUEST:
                    HandleStockSnapshotRequest(thread, 
                            thread->clients[i],request->ticker());
                    break;
                default:
                    break;
            }
            
            delete request;
            
            thread->client_count--;
            close(thread->clients[i]);
            thread->clients[i]=0;
            break; 
            
        } // End if when client request recieved
    } // End loop for checking each client
        
        
    } // End forever loop
}

void HandleStockSnapshotRequest(Thread *thread, int fd, const std::string &ticker)
{
    protocol::StockPublisherResponse response;
    response.set_requesttype(protocol::STOCKSNAPSHOTRESPONSE);
    response.set_ticker(ticker);
    
    std::string company = thread->server->f_ptrGetCompanyName(ticker);
    std::list<types::StockData*>* data = 
            thread->server->f_ptrGetStockDataByTicker(ticker);
 
    if(company.empty() || data == NULL)
    {
        response.set_name("null");
        response.set_statusmessage(protocol::TICKERDOESNOTEXIST);
    }
    
    std::list<types::StockData*>::iterator it = data->begin();
    
    while(it != data->end())
    {
        protocol::StockPublisherResponse_PriceData*
                pricePoints = response.add_pricedatapoints();
        
        pricePoints->set_close((*it)->get_close());
        pricePoints->set_open((*it)->get_open());
        pricePoints->set_high((*it)->get_high());
        pricePoints->set_low((*it)->get_low());
        pricePoints->set_date((*it)->get_date());
        pricePoints->set_volume((*it)->get_volume());
    }
    
    response.SerializeToFileDescriptor(fd);
}
//</editor-fold>
}}}
